import os
import time
import json
import logging
import requests
import subprocess
import threading
from datetime import datetime

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    filename='sync_slave.log'
)

# 从服务器配置
class Config:
    # 主服务器API地址
    MASTER_API_URL = 'http://主服务器IP:5000/api/commands'
    # 认证密钥（与主服务器一致）
    AUTH_KEY = 'your_secure_api_key'
    # 同步间隔（秒）
    SYNC_INTERVAL = 30
    # 本地目录映射（主服务器路径:从服务器路径）
    PATH_MAPPING = {
        '/www/wwwroot/': '/www/wwwroot/',
        '/www/server/panel/vhost/nginx/': '/www/server/panel/vhost/nginx/',
        '/www/server/panel/config/': '/www/server/panel/config/'
    }
    # 执行命令后需要重启的服务
    SERVICES_TO_RESTART = {
        '/www/server/panel/vhost/nginx/': ['nginx'],
        '/www/server/panel/config/': ['bt']
    }

# 同步客户端
class BtSyncClient:
    def __init__(self):
        self.headers = {'Authorization': f'Bearer {Config.AUTH_KEY}'}
        self.executed_commands = set()
        self.restart_required = set()
    
    def sync(self):
        """从主服务器获取指令并执行"""
        try:
            # 获取待执行指令
            response = requests.get(Config.MASTER_API_URL, headers=self.headers)
            if response.status_code != 200:
                logging.error(f"获取指令失败: HTTP {response.status_code}")
                return
            
            commands = response.json()
            if not commands:
                logging.debug("没有新指令")
                return
            
            logging.info(f"获取到 {len(commands)} 条新指令")
            
            # 执行指令
            executed_ids = []
            for i, command in enumerate(commands):
                if self._execute_command(command):
                    executed_ids.append(i)
            
            # 清除已执行的指令
            if executed_ids:
                self._clear_executed_commands(executed_ids)
            
            # 重启需要的服务
            self._restart_services()
            
        except Exception as e:
            logging.error(f"同步过程发生错误: {e}")
    
    def _execute_command(self, command):
        """执行单个指令"""
        action = command['action']
        master_path = command['path']
        
        # 映射到本地路径
        local_path = self._map_path(master_path)
        if not local_path:
            logging.warning(f"无法映射路径: {master_path}")
            return False
        
        try:
            # 创建必要的目录
            if action != 'delete' and not os.path.exists(os.path.dirname(local_path)):
                os.makedirs(os.path.dirname(local_path), exist_ok=True)
            
            # 执行操作
            if action == 'create' or action == 'modify':
                # 从主服务器复制文件
                self._copy_file_from_master(master_path, local_path)
                logging.info(f"执行: {action} {local_path}")
                
                # 检查是否需要重启服务
                self._check_service_restart(master_path)
                
            elif action == 'delete':
                # 删除本地文件
                if os.path.exists(local_path):
                    if os.path.isdir(local_path):
                        os.rmdir(local_path)
                    else:
                        os.remove(local_path)
                    logging.info(f"执行: delete {local_path}")
                    
                    # 检查是否需要重启服务
                    self._check_service_restart(master_path)
                else:
                    logging.warning(f"删除失败，文件不存在: {local_path}")
            
            return True
            
        except Exception as e:
            logging.error(f"执行指令失败: {action} {local_path}, 错误: {e}")
            return False
    
    def _map_path(self, master_path):
        """将主服务器路径映射到从服务器路径"""
        for master_prefix, local_prefix in Config.PATH_MAPPING.items():
            if master_path.startswith(master_prefix):
                return local_prefix + master_path[len(master_prefix):]
        return None
    
    def _copy_file_from_master(self, master_path, local_path):
        """从主服务器复制文件到本地"""
        # 使用scp命令通过SSH复制文件
        cmd = f"scp root@{Config.MASTER_API_URL.split('//')[1].split(':')[0]}:{master_path} {local_path}"
        result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
        
        if result.returncode != 0:
            logging.error(f"复制文件失败: {cmd}, 错误: {result.stderr}")
            raise Exception(f"复制文件失败: {result.stderr}")
    
    def _check_service_restart(self, path):
        """检查文件路径是否需要重启相关服务"""
        for dir_prefix, services in Config.SERVICES_TO_RESTART.items():
            if path.startswith(dir_prefix):
                self.restart_required.update(services)
    
    def _restart_services(self):
        """重启需要的服务"""
        if not self.restart_required:
            return
        
        logging.info(f"准备重启服务: {', '.join(self.restart_required)}")
        for service in self.restart_required:
            try:
                cmd = f"/etc/init.d/{service} restart"
                result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
                
                if result.returncode != 0:
                    logging.error(f"重启服务失败: {service}, 错误: {result.stderr}")
                else:
                    logging.info(f"成功重启服务: {service}")
            except Exception as e:
                logging.error(f"重启服务发生异常: {service}, 错误: {e}")
        
        self.restart_required.clear()
    
    def _clear_executed_commands(self, executed_ids):
        """通知主服务器清除已执行的指令"""
        try:
            data = {'executed_ids': executed_ids}
            response = requests.post(
                f"{Config.MASTER_API_URL}/clear",
                json=data,
                headers=self.headers
            )
            
            if response.status_code != 200:
                logging.error(f"清除指令失败: HTTP {response.status_code}")
        except Exception as e:
            logging.error(f"清除指令发生错误: {e}")

# 启动同步客户端
def start_sync_client():
    client = BtSyncClient()
    logging.info(f"启动宝塔配置同步客户端，同步间隔: {Config.SYNC_INTERVAL}秒")
    
    while True:
        try:
            client.sync()
        except Exception as e:
            logging.error(f"同步循环异常: {e}")
        time.sleep(Config.SYNC_INTERVAL)

if __name__ == '__main__':
    logging.info("启动宝塔配置同步从服务器...")
    start_sync_client()    